package com.demo.mybatis.typehandle.support;

import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.demo.mybatis.typehandle.entity.UserExtra;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

@MappedTypes({UserExtra.class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class CustomJacksonTypeHandler extends BaseTypeHandler<UserExtra> {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    protected final Class<?> type;
    protected Type genericType;

    public CustomJacksonTypeHandler(Class<?> type) {
        this.type = type;
        Assert.notNull(type, "Type argument cannot be null");
    }

    public CustomJacksonTypeHandler(Class<?> type, Field field) {
        this(type);
        this.genericType = field.getGenericType();
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, UserExtra parameter, JdbcType jdbcType) throws SQLException {
        try {
            ps.setString(i, OBJECT_MAPPER.writeValueAsString(parameter));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public UserExtra getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String json = rs.getString(columnName);
        return parse(json);
    }

    @Override
    public UserExtra getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String json = rs.getString(columnIndex);
        return parse(json);
    }

    @Override
    public UserExtra getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String json = cs.getString(columnIndex);
        return parse(json);
    }

    private UserExtra parse(String json) {
        TypeFactory typeFactory = OBJECT_MAPPER.getTypeFactory();
        JavaType javaType = typeFactory.constructType(getFieldType());
        try {
            return StringUtils.isBlank(json) ? null : OBJECT_MAPPER.readValue(json, javaType);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public Type getFieldType() {
        return this.genericType != null ? this.genericType : this.type;
    }
}
